import random

class Task:
    def init_for_sim(self, env, request, task_id, parent_task, ms_kind, rpc_id, rpc_type, rt, start_time_in_trace_data,print_level):
        #仿真环境
        self.env=env
        #输出水平
        self.print_level=print_level
        #请求对象
        self.request=request
        #任务ID
        self.task_id=task_id
        #分区
        self.part_id=None
        #上一级任务
        self.parent_task=parent_task
        #任务对应的微服务类型
        self.ms_kind=ms_kind
        #请求类型，决定是否等待返回值
        self.rpc_type=rpc_type
        #请求ID
        self.rpc_id=rpc_id
        
        #trace中的开始时间
        self.start_time_in_trace_data=start_time_in_trace_data
        #下一级任务list
        self.sub_task_list=[]
        #记录所有父task所在的微服务ID，禁止放到和父task相同的微服务上，防止锁死
        # self.prohibit_ms_id_list=[]
        #记录当前task目标的微服务
        self.aim_ms_obj_list=[]
        self.first_parent_ms_obj=None
        #记录task是否故障
        self.fault_flage = False
        self.fault_tolerance_num=None
        self.replica_num=None
        #是否已经生成下一级的标志，用于避免由于父task多副本导致的子task更多地被生成
        self.generate_next_tasks_flage=False
        #由于多副本，判断是否完成，并发送信息给request
        self.done_flage=False
        #用于多副本判断当前task是否向父task返回了请求。
        self.back_flage=False
        
        self.reject_flage=False
        #用于记录有多少子task返回结果了
        self.back_task_num=0
        #用于记录需要多少子task返回结果
        self.need_back_task_num=0
        #返回事件，结合上述子task返回数量使用。用于仿真环境等待，
        self.can_back_event = self.request.env.event()
        
        # self.update_prohibit_flage=False
        self.update_base_info_flage=False
        #响应时间，也叫处理时间
        self.deal_time=rt
        self.ave_deal_time=None     #基于历史数据，用于调度
        #相较于父task的开始延后时间
        self.pre_time=0
        #相较于父task的结束提前时间
        # self.suc_time=0
        #数据传输量(需要证据)
        
        self.tt_datas_bef=random.randint(1,10)*20*1024*1024/1000
        self.tt_datas_aft=random.randint(1,10)*20*1024*1024/1000
        self.part_id=None
        #record for our method
        self.real_arrive_queue_time_point={}
        # self.real_start_deal_time_point={}
        # self.real_waiting_time={}
        # self.real_deal_time={}
        
        self.sub_deadline=None
        # self.index_for_replica_record=0   #用于区分多个副本同时调度到同一个微服务上
        
        return
    
    def init_for_analyze(self, request, task_id, parent_task, ms_kind, rpc_id, rpc_type, rt, start_time_in_trace_data):
        #任务ID
        self.task_id=task_id
        #任务对应的微服务类型
        self.ms_kind=ms_kind
        #请求类型，决定是否等待返回值
        self.rpc_type=rpc_type
        #请求ID
        self.rpc_id=rpc_id
        
        self.start_time_in_trace_data=start_time_in_trace_data
        #下一级任务list
        self.sub_task_list=[]

        self.need_back_task_num=0
        
        #响应时间，也叫处理时间
        self.deal_time=rt
        #相较于父task的开始延后时间
        self.pre_time=0
        #相较于父task的结束提前时间
        # self.suc_time=0
        
        self.got_replica_flage=False
        self.replica_num=None
        
        #记录所有子task的数量
        self.all_sub_task_num=0
        
        self.reset_as_base(request,parent_task)
        return
    
    #重置为模板
    def reset_as_base(self,request,parent_task):
        self.request=request
        self.parent_task=parent_task
        self.call_num=1
        self.sum_deal_time=self.deal_time
        self.sum_pre_time=self.pre_time
        # self.sum_suc_time=self.suc_time
        self.sum_first_sub_task_num=len(self.sub_task_list)
        self.uprank=None
        self.sub_deadline=None
        
    #更新禁止的MS id,避免调度死锁
    # def update_prohibit_ms_id_list(self):
    #     if self.update_prohibit_flage==False:
    #         self.update_prohibit_flage=True
    #         if self.parent_task!=None:
    #             self.prohibit_ms_id_list=self.parent_task.prohibit_ms_id_list[:]  
    #             for ms_obj in self.parent_task.aim_ms_obj_list:
    #                 self.prohibit_ms_id_list.append(ms_obj.ms_id)
            
    #设置子任务列表
    def set_sub_task_list(self,sub_task_list):
        self.sub_task_list=sub_task_list
        for task in self.sub_task_list:
            if task.rpc_type!="mq":
                self.need_back_task_num+=1
        return
    
    #设置副本数量
    def set_replica_num(self, num):
        self.replica_num=num
        self.fault_tolerance_num=num
        
        
    #故障发生后，设置当前task为故障，被微服务调用设置
    def task_with_error(self):
        self.fault_tolerance_num-=1
        if self.fault_tolerance_num<=0:
            self.fault_flage=True
            
    #task被完成，并执行返回
    def end_and_back(self, ms_id, end_time):
        # self.record_for_end_deal(ms_id, end_time)
        #记录task为完成
        if self.done_flage==False:
            self.request.done_one_task()
            self.done_flage=True
        ##处理任务返回给上一级任务
        self.send_back()
        
    #task处理完成后，执行返回
    def send_back(self):
        if self.reject_flage==True:
            self.request.back_with_reject()
            return
        if self.back_flage==False:
            self.back_flage=True
            if self.parent_task==None:
                self.__back_to_request()
            elif self.rpc_type!="mq":
                self.__back_to_parent()

    #返回给请求
    def __back_to_request(self):
        self.request.back_one_task(self.fault_flage)
        
    #返回给父task    
    def __back_to_parent(self):
        self.parent_task.receive_one_back_task(self.fault_flage, 0) #self.suc_time
        
    #作为父task，接受来自子任务的返回，更改父task的失败信息和继续处理的时长    
    def receive_one_back_task(self, fault_flage, end_delay_time):
        if fault_flage:
            self.fault_flage=True
        self.env.process(self.__receive_one_back_task_delay(end_delay_time))
    
    #用于延时等待，触发可返回事件    
    def __receive_one_back_task_delay(self, end_delay_time):
        self.back_task_num+=1
        yield self.env.timeout(end_delay_time)
        if self.back_task_num==self.need_back_task_num and self.can_back_event.triggered==False:
            self.can_back_event.succeed()
    
    #base 融合普通请求信息（分析）    
    def merge(self,task):
        self.call_num+=1
        #以下数据先求和，后面会求平均
        self.sum_deal_time+=task.deal_time
        self.sum_pre_time+=task.pre_time
        # self.sum_suc_time+=task.suc_time
        self.sum_first_sub_task_num+=len(task.sub_task_list)
        assert task.deal_time>=1
        
       
    #更新平均的任务三段时间，和一级后继任务数量（分析）
    def merge_end(self):
        assert self.call_num<=self.sum_deal_time
        
        if self.call_num==1:
            self.ave_deal_time=self.deal_time
            self.ave_pre_time=self.pre_time
            # self.ave_suc_time=self.suc_time
            self.ave_first_sub_task_num=len(self.sub_task_list)
        else:
            self.ave_deal_time=self.sum_deal_time/self.call_num
            self.ave_pre_time=self.sum_pre_time/self.call_num
            # self.ave_suc_time=self.sum_suc_time/self.call_num
            self.ave_first_sub_task_num=self.sum_first_sub_task_num/self.call_num
        return
    

            
    
    
    #由于存在副本的关系，这里需要传入ms_id以示区别 
    #如果是在同一个ms_id上的，直接覆盖就行   
    # def record_for_cold_start(self, ms_id, time_length):
    #     self.real_cold_start_time[ms_id]=time_length
           
    def record_arrive_queue_time_point(self, ms_id, time_now):
        if ms_id in  self.real_arrive_queue_time_point.keys():
            self.real_arrive_queue_time_point[ms_id].append(time_now)
        else:
            self.real_arrive_queue_time_point[ms_id]=[time_now]
    
    def get_arrive_queue_time_point(self, ms_id):
        return self.real_arrive_queue_time_point[ms_id].pop(0)
        
        
    # def record_for_start_deal(self, ms_id, time_now):
    #     # self.real_waiting_time[ms_id]=time_now-self.real_arrive_queue_time_point[ms_id]
    #     self.real_start_deal_time_point[ms_id]=time_now
        
    # def record_for_back_deal(self, ms_id, time_now):
    #     self.real_back_deal_time_point[ms_id]=time_now
        
    # def record_for_back(self, ms_id, time_now):
    #     self.real_deal_time[ms_id]=time_now-self.real_start_deal_time_point[ms_id]
        
    def get_early_end_time(self, start_time):
        early_end_time=start_time+self.deal_time
        for task_suc in self.sub_task_list:
            time_t=task_suc.get_early_end_time(start_time+task_suc.pre_time)+task_suc.suc_time
            early_end_time=max(time_t, early_end_time)
        return early_end_time
    
    #获取最大最小makespan，用于生成deadline
    def get_expect_end_time(self, min_start_time, max_start_time, cloud_system):
        min_end_time=min_start_time+self.deal_time
        max_end_time=max_start_time+self.deal_time+cloud_system.get_cold_start_time(self.ms_kind)
        min_t=min_start_time
        max_t=max_start_time+cloud_system.get_cold_start_time(self.ms_kind)   #冷启动时间
        for task_suc in self.sub_task_list:
            min_end_time_back, max_end_time_back=task_suc.get_expect_end_time(min_t+task_suc.pre_time, max_t+task_suc.pre_time, cloud_system)
            min_end_time=max(min_end_time, min_end_time_back) #+task_suc.suc_time
            max_end_time=max(max_end_time, max_end_time_back)#task_suc.suc_time
        return min_end_time, max_end_time
        
    
        
#######################################################for scheduler ########################################################        
    
    def set_early_start_latest_end_time(self, early_start_time, latest_end_time):
        
        self.early_start_time=early_start_time
        self.latest_end_time=latest_end_time
        #由于Call Graph中任务数量还没确定，因此这里得到的最早开始时间不一定小于最晚结束时间，统计这个只是用于参考
        # assert self.early_start_time<=self.latest_end_time
        
        for task_t in self.sub_task_list:
            early_start_time_sub=task_t.pre_time+early_start_time
            latest_end_time_sub=latest_end_time-task_t.suc_time
            task_t.set_early_start_latest_end_time(early_start_time_sub,latest_end_time_sub)
            
            
            
    # #更新任务后继所有任务的数量
    # def update_all_sub_task_num(self):
    #     self.all_sub_task_num=0
    #     for task in self.sub_task_list:
    #         task.update_all_sub_task_num()
    #         self.all_sub_task_num=self.all_sub_task_num+1+task.all_sub_task_num
            
    # def update_task_id_info_to_request(self):
    #     self.request.task_id_list.append(self.task_id)
    #     self.request.task_id_to_obj[self.task_id]=self
    #     for task_t in self.sub_task_list:
    #         task_t.update_task_id_info_to_request()